#define SCREEN_MEM 0xb8000	
#define SCREEN_WIDTH 80
#define SCREEN_HEIGHT 25

#include <system.h>

/* Static vars to hold the current cursor position */
static unsigned int screen_x; 
static unsigned int screen_y;
static unsigned short *vmem;
static unsigned char textcolor;

void init_video() {
	vmem = (unsigned short *)SCREEN_MEM;
	settextcolor(C_WHITE, C_BLACK);		/* White on black */
	cls();
}

/* Clear the entire screen */
void cls() {
	unsigned int i = 0;
	while (i < (SCREEN_WIDTH * SCREEN_HEIGHT)) {
		vmem[i] = ' ' | (textcolor << 8);
		i++;
	}
	screen_x = 0;
	screen_y = 0;
	updcsr();
}

/* Print the given character to the screen, starting at the current 
   cursor position, and scroll or update the cursor position as needed.
*/
void putch(char ch)
{
	unsigned int offset = 0;

	/* Handle the char we're given */
	switch (ch) 
	{
		case '\n':	/* CRLF */
			screen_x = 0;
			screen_y++;
			break;
		case '\b':	/* backspace */
			if (screen_x > 0) screen_x--;
			break;
		case '\t':	/* tab */
			screen_x = (screen_x + 8) & ~(8 - 1);
			break;
		default:
			offset = (screen_y * SCREEN_WIDTH) + (screen_x);
			vmem[offset] = ch | (textcolor << 8);
			screen_x++;
	}
	
	/* If we've reached the end of a line, move to the next one */
	if (screen_x >= SCREEN_WIDTH) 
	{
		screen_x = 0;
		screen_y++;
	}

	/* Scroll the screen vertically if need be */
	scroll();

	/* Move the cursor */
	updcsr();
}

/* Print the given string to the screen starting at the current cursor position */
void puts(char *str) 
{
	int i;
	for (i = 0; i < strlen(str); i++)
	{
		putch(str[i]);
	}
}

/* Scrolls the screen up one line if need be */
void scroll()
{
	unsigned short blank = ' ' | (textcolor << 8);
	int rowcount;
	if (screen_y >= SCREEN_HEIGHT)
	{
		rowcount = screen_y - SCREEN_HEIGHT + 1;
		memcpy(vmem, vmem + (rowcount * SCREEN_WIDTH), (SCREEN_HEIGHT - rowcount) * SCREEN_WIDTH * 2);

		memsetw(vmem + (SCREEN_HEIGHT - rowcount) * SCREEN_WIDTH, blank, SCREEN_WIDTH);
		screen_y = SCREEN_HEIGHT - 1;
	}
}

void settextcolor(unsigned char forecolor, unsigned char backcolor)
{
	/* Top 4 bits = background. Bottom 4 bits = foreground */
	textcolor = (backcolor << 4) | (forecolor & 0x0F);
}

/* Draw a blinking cursor on the screen at the current cursor position */
void updcsr()
{
	unsigned int pos = (screen_y * SCREEN_WIDTH) + screen_x;

	outportb(0x3d4, 14);
	outportb(0x3d5, pos >> 8);
	outportb(0x3d4, 15);
	outportb(0x3d5, pos);
}
